import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;


public class SummaryGenerator {
	//Video
	protected VideoFile inputVideoFile = null;
	protected FileOutputStream outputVideoStream;
	protected String outputVideoName;
	
	//Sound
	protected AudioFile inputAudioFile = null;
	protected File outputAudioFile;
	protected AudioFileFormat.Type audioType;
	protected AudioFormat audioFormat;
	protected String outputAudioName;
	protected int totalFrameSize;
	
	public SummaryGenerator(String inputVideoName, String inputAudioName){
		//Open video file
		inputVideoFile = new VideoFile(inputVideoName);
		outputVideoName = "summary.576v";
		
		//Open audio file		
		try {
			File inputAudio = new File(inputAudioName);
			AudioInputStream ais = AudioSystem.getAudioInputStream(inputAudio);
			audioType = AudioSystem.getAudioFileFormat(inputAudio).getType();
			audioFormat = ais.getFormat();
			
			inputAudioFile = new AudioFile(inputAudioName);
			inputAudioFile.setVideoFrameIntervalDuration(1/(float)inputVideoFile.getFps());
			
			//Get the sound total sample length
			Clip clip = AudioSystem.getClip();
			clip.open(ais);
			totalFrameSize = clip.getFrameLength();
			clip.close();
			outputAudioName = "summary.wav";
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (LineUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void generateFiles(VideoSegment[] shots){
		if (!generateVideoFile(shots)){
			System.out.println("Output video file error!");
		}
		if (!generateAudioFile(shots)){
			System.out.println("Output audio file error!");
		}
	}
	
	protected boolean generateVideoFile(VideoSegment[] shots){
		//Video file output
		if (inputVideoFile.getTotalFrameNumber() <= 0){
			System.out.println("Error! Failed to open the video file in GenerateFiles()!");
			return false;
		}
		
		File video = new File(outputVideoName);
		try {
			outputVideoStream = new FileOutputStream(video);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Video ouput starts ******");
		
		//Start output
		for(int i=0; i<shots.length; ++i){
			inputVideoFile.setFramePosition(shots[i].start);
			if( inputVideoFile.getTotalFrameNumber() <= shots[i].end){
				//Out of boundary
				continue;
			}
			try {
				while(inputVideoFile.getCurrentFrameIndex() <= shots[i].end){					
					outputVideoStream.write(inputVideoFile.readRawData());					
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return false;
			}
		}
		
		//Close the file
		try {
			outputVideoStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Video output done ******");
		
		return true;
	}
	/************************************************************************
	 * Generate the audio file based on the camera shots
	 * @param shots
	 * @return
	 */
	protected boolean generateAudioFile(VideoSegment[] shots){
		if(totalFrameSize <= 0){
			System.out.println("Error! Failed to open the audio file in GenerateFiles()!");
			return false;
		}
		
		//Initialize for output		
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		byte[] rawBuffer;
		
		//Store the data in the stream
		try {
			for(int i=0; i<shots.length; ++i){
				//Set the start position
				inputAudioFile.setPositionBasedOnVideo(shots[i].start);				
				rawBuffer = inputAudioFile.readDurationSoundData(shots[i].end - shots[i].start + 1);
				if (rawBuffer != null)
					byteArrayOutputStream.write(rawBuffer, 0, rawBuffer.length);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		try {
			byteArrayOutputStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Audio output starts ******");
		
		byte[] audioData = byteArrayOutputStream.toByteArray();
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
		AudioInputStream ais = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length/audioFormat.getFrameSize());
		
		outputAudioFile = new File(outputAudioName);
		try {
			AudioSystem.write(ais, audioType, outputAudioFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Audio output done ******");
		
		return true;
	}
	
	protected AudioClip[] convert(VideoSegment[] shots){
		AudioClip[] clips = new AudioClip[shots.length];
		double factor = audioFormat.getFrameRate() / inputVideoFile.getFps() * audioFormat.getFrameSize();
		for(int i=0; i<shots.length; ++i){
			int start = (int) (shots[i].start * factor);
			int end = (int) (shots[i].end * factor);
			clips[i] = new AudioClip(start, end);
		}
		return clips;
	}
	
	/**********************************************************************************
	 * Generate the audio file based on conversion from camera shots to audio clips
	 * @param clips
	 * @return
	 */
	protected boolean generateAudioFile(AudioClip[] clips){
		if(totalFrameSize <= 0){
			System.out.println("Error! Failed to open the audio file in GenerateFiles()!");
			return false;
		}
		
		//Initialize for output		
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		int frameSize = audioFormat.getFrameSize();
		int unitSize = (int) (audioFormat.getFrameRate()*frameSize/inputVideoFile.getFps());
		byte[] rawBuffer = new byte[unitSize];
		
		//Store the data in the buffer
		for(int i=0; i<clips.length; ++i){
			//Set the start position
			inputAudioFile.setPositionBasedOnSound(clips[i].start*frameSize);
			int count = clips[i].end - clips[i].start + 1;
			while(count>0){
				//While not the end position
				inputAudioFile.readSoundData(rawBuffer);
				byteArrayOutputStream.write(rawBuffer, 0, unitSize);
				--count;
			}
		}
		try {
			byteArrayOutputStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Audio output starts ******");
		
		byte[] audioData = byteArrayOutputStream.toByteArray();
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
		AudioInputStream ais = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length/frameSize);
		
		outputAudioFile = new File(outputAudioName);
		try {
			AudioSystem.write(ais, audioType, outputAudioFile);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		System.out.println("****** Audio output done ******");
		
		return true;
	}
	
	class AudioClip{
		public int start;  //Frame index
		public int end;    //Frame index
		
		public AudioClip(int start, int end){
			this.start = start;
			this.end = end;
		}
	}
}
